﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Data.Common;
using System.Web;
using System.Diagnostics;
using Microsoft.Practices.Unity;
using Microsoft.Practices.ServiceLocation;
using MvcExtensions;
using MvcExtensions.Unity;

namespace mForum.Web
{
    using mForum.Core.Common;
    using mForum.Core.Infrastructure;
    using mForum.Core.Infrastructure.Configuration;
    using mForum.Core.Infrastructure.Configuration.ConfigurationSections;
    using mForum.Core.Infrastructure.Settings;
    using mForum.Core.Infrastructure.Resource;
    using mForum.Core.Service;
    using mForum.Core.Repository;
    using mForum.Domain.Repository;
    using mForum.Services;
    using mForum.Resources;
    using mForum.Infrastructure.NHibernate;
    using mForum.Infrastructure.NHibernate.Repository;
    using mForum.Web.Common.Infrastructure;
    using mForum.Web.Common.Infrastructure.Cache;
    using mForum.Web.Common.Infrastructure.AssetManager;
    using mForum.Web.Common.ViewDataModel;

    public class RegisterServices : IModule
    {
        private static readonly Func<LifetimeManager> transient = () => new TransientLifetimeManager();
        private static readonly Func<LifetimeManager> perRequest = () => new PerRequestLifetimeManager();
        private static readonly Func<LifetimeManager> singleton = () => new ContainerControlledLifetimeManager();

        public void Load(IUnityContainer container)
        {
            Check.Argument.IsNotNull(container, "container");

            container.RegisterType<IConfigurationWrapper, ConfigurationWrapper>(singleton())
                     .RegisterType<IResourceManagerFactory, ResourceManagerFactory>(singleton())
                     .RegisterType<IViewDataFactory, ViewDataFactory>()
                     .RegisterType<IFormsAuthenticationService, FormsAuthenticationService>(singleton())
                     .RegisterType<IUserService, UserService>(perRequest())
                     .RegisterType<IHttpContextProvider, HttpContextProvider>(perRequest())
                     .RegisterType<ISessionManager, SessionManager>(perRequest())
                     .RegisterType<ICache, Cache>(singleton(), new InjectionConstructor(HttpRuntime.Cache))
                     .RegisterType<IAssetManager, AssetManager>(perRequest())
                     .RegisterType<IFileWrapper, FileWrapper>(singleton())
                     .RegisterType<IVirtualPathUtility, VirtualPathUtilityWrapper>(singleton());
                     
            IResourceManagerFactory resourceManagerFactory = container.Resolve<IResourceManagerFactory>();
            ResourceHelper.Initialize(resourceManagerFactory.CreateResourceManager());

            IConfigurationWrapper configManager = container.Resolve<IConfigurationWrapper>();
            configManager.SetConnectionString("ApplicationServices");

            Settings settings = InitializeSettings(configManager, container.Resolve<HttpContextBase>());

            IBuildManager buildManager = container.Resolve<IBuildManager>();

            container.RegisterType<IDatabaseFactory, DatabaseFactory>(perRequest())
                     .RegisterType<IUnitOfWork, UnitOfWork>(perRequest())
                     .RegisterInstance(settings);
                     

            //register repositories
            RegisterRepositories(buildManager, container);
        }

        private static void RegisterRepositories(IBuildManager buildManager, IUnityContainer container)
        {
            Type genericRepositoryType = typeof(IRepository<>);

            IEnumerable<Type> repositoryContractTypes = buildManager.PublicTypes.Where(type => (type != null) && type.IsInterface && type.GetInterfaces().Any(interfaceType => interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition().Equals(genericRepositoryType))).ToList();

            foreach (Type repositoryImplementationType in buildManager.ConcreteTypes.Where(implementationType => repositoryContractTypes.Any(contractType => contractType.IsAssignableFrom(implementationType))))
            {
                foreach (Type repositoryInterfaceType in repositoryImplementationType.GetInterfaces())
                {
                    container.RegisterType(repositoryInterfaceType, repositoryImplementationType, perRequest());
                }
            }
        }

        private static Settings InitializeSettings(IConfigurationWrapper configurationWrapper, HttpContextBase httpContext)
        {
            SettingsConfigurationSection settingsSection = configurationWrapper.GetSection<SettingsConfigurationSection>(SettingsConfigurationSection.SectionName);

            Settings settings = new Settings(settingsSection.SiteTitle, settingsSection.MetaKeywords, settingsSection.MetaDescription, settingsSection.MinPasswordLength);

            return settings;
        }
    }
}